//! pl011 硬件实现
#![feature(lazy_cell)]
#![allow(unsafe_code)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]

mod trace;

use std::{
    any::Any,
    sync::{Arc, Mutex},
};

use ::clock::HwClockInput;
use chardev::{CharBackendDevice, CharBackendImpl};
use clock::ClkEventImpl;
use device::{DeviceManageImpl, DeviceResetType};
use interrupt::IntBackendDevice;
use memory::MemoryRegionImpl;
use objectid::ObjectId;
use trace::Pl011StateTrace;
use tracelog::{log_guest_error, log_unimp};

const PL011_INT_TX: u32 = 0x20;
const PL011_INT_RX: u32 = 0x10;

const PL011_FLAG_TXFE: u32 = 0x80;
const PL011_FLAG_RXFF: u32 = 0x40;
const PL011_FLAG_TXFF: u32 = 0x20;
const PL011_FLAG_RXFE: u32 = 0x10;

// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC
const INT_OE: u32 = 1 << 10;
const INT_BE: u32 = 1 << 9;
const INT_PE: u32 = 1 << 8;
const INT_FE: u32 = 1 << 7;
const INT_RT: u32 = 1 << 6;
const INT_TX: u32 = 1 << 5;
const INT_RX: u32 = 1 << 4;
const INT_DSR: u32 = 1 << 3;
const INT_DCD: u32 = 1 << 2;
const INT_CTS: u32 = 1 << 1;
const INT_RI: u32 = 1 << 0;
const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE;
const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS;

const PL011_ID_ARM: [u8; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
const PL011_ID_LUMINARY: [u8; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1];

const IRQMASK: [u32; 6] = [
    INT_E | INT_MS | INT_RT | INT_TX | INT_RX, // combined IRQ
    INT_RX,
    INT_TX,
    INT_RT,
    INT_MS,
    INT_E,
];

#[derive(Default)]
struct Pl011StateInner {
    flags: u32,
    lcr: u32,
    rsr: u32,
    cr: u32,
    dmacr: u32,
    int_enabled: u32,
    int_level: u32,
    read_fifo: [u32; 16],
    ilpr: u32,
    ibrd: u32,
    fbrd: u32,
    ifl: u32,
    read_pos: usize,
    read_count: i32,
    read_trigger: i32,
    id: [u8; 8],
}

impl Pl011StateInner {
    fn pl011_update(&self, interface: &Pl011StateInterface) {
        let flags = self.int_level & self.int_enabled;

        Pl011StateTrace::trace_pl011_irq_state(&(flags != 0));
        for (i, irq) in interface.irq.iter().enumerate() {
            irq.set_int(flags & IRQMASK[i] != 0);
        }
    }

    fn pl011_is_fifo_enabled(&self) -> bool {
        self.lcr & 0x10 != 0
    }

    #[inline]
    fn pl011_get_fifo_depth(&self) -> usize {
        if self.pl011_is_fifo_enabled() { 16 } else { 1 }
    }

    #[inline]
    fn pl011_reset_fifo(&mut self) {
        self.read_count = 0;
        self.read_pos = 0;

        // Reset FIFO flags
        self.flags &= !(PL011_FLAG_RXFF | PL011_FLAG_TXFF);
        self.flags |= PL011_FLAG_RXFE | PL011_FLAG_TXFE;
    }

    fn pl011_set_read_trigger(&mut self) {
        self.read_trigger = 1;
    }

    fn pl011_get_baudrate(&self, interface: &Pl011StateInterface) -> u64 {
        if self.ibrd == 0 {
            return 0;
        }

        let clk = interface.clk_input.clock_get_hz();
        (clk / (((self.ibrd as u64) << 6) + self.fbrd as u64)) << 2
    }

    fn pl011_trace_baudrate_change(&self, interface: &Pl011StateInterface) {
        Pl011StateTrace::trace_pl011_baudrate_change(
            &self.pl011_get_baudrate(interface),
            &interface.clk_input.clock_get_hz(),
            &self.ibrd,
            &self.fbrd,
        );
    }
}

struct Pl011StateInterface {
    irq: [Arc<IntBackendDevice>; 6],
    char_backend_device: Arc<CharBackendDevice>,
    clk_input: Arc<HwClockInput>,
}

/// pl011 硬件管理
pub struct Pl011State {
    inner: Arc<Mutex<Pl011StateInner>>,
    interface: Box<Pl011StateInterface>,
    name: String,
}

unsafe impl Send for Pl011State {}
unsafe impl Sync for Pl011State {}

impl Pl011State {
    fn pl011_put_fifo(&self, value: u32) {
        let mut inner = self.inner.lock().unwrap();

        let pipe_depth = inner.pl011_get_fifo_depth();
        let slot =
            (inner.read_pos.wrapping_add_signed(inner.read_count as isize)) & (pipe_depth - 1);
        inner.read_fifo[slot] = value;
        inner.read_count += 1;
        inner.flags &= !PL011_FLAG_RXFE;
        Pl011StateTrace::trace_pl011_put_fifo(&(value as u64), &inner.read_count);
        if inner.read_count == pipe_depth as i32 {
            Pl011StateTrace::trace_pl011_put_fifo_full();
            inner.flags |= PL011_FLAG_RXFF;
        }
        if inner.read_count == inner.read_trigger {
            inner.int_level |= PL011_INT_RX;
            inner.pl011_update(&self.interface);
        }
    }

    fn reset(&self) {
        let mut inner = self.inner.lock().unwrap();
        inner.lcr = 0;
        inner.rsr = 0;
        inner.dmacr = 0;
        inner.int_enabled = 0;
        inner.int_level = 0;
        inner.ilpr = 0;
        inner.ibrd = 0;
        inner.fbrd = 0;
        inner.read_trigger = 1;
        inner.ifl = 0x12;
        inner.cr = 0x300;
        inner.flags = 0;
        inner.pl011_reset_fifo();
    }
}

impl MemoryRegionImpl for Pl011State {
    fn read(&self, offset: u64, _: usize) -> u64 {
        let mut inner = self.inner.lock().unwrap();

        let value = match offset >> 2 {
            // UARTDR
            0 => {
                inner.flags &= !PL011_FLAG_RXFF;
                let c = inner.read_fifo[inner.read_pos];
                if inner.read_count > 0 {
                    inner.read_count -= 1;
                    inner.read_pos = (inner.read_pos + 1) & (inner.pl011_get_fifo_depth() - 1);
                }
                if inner.read_count == 0 {
                    inner.flags |= PL011_FLAG_RXFE;
                }
                if inner.read_count == inner.read_trigger - 1 {
                    inner.int_level &= !PL011_INT_RX;
                }
                Pl011StateTrace::trace_pl011_read_fifo(&inner.read_count);
                inner.rsr = c >> 8;
                inner.pl011_update(&self.interface);
                self.interface.char_backend_device.chr_notify_accept_input();
                c
            },
            // UARTRSR
            1 => inner.rsr,

            // UARTFR
            6 => inner.flags,
            // UARTILPR
            8 => inner.ilpr,
            // UARTIBRD
            9 => inner.ibrd,
            // UARTFBRD
            10 => inner.fbrd,
            // UARTLCR_H
            11 => inner.lcr,
            // UARTCR
            12 => inner.cr,
            // UARTIFLS
            13 => inner.ifl,
            // UARTIMSC
            14 => inner.int_enabled,
            // UARTRIS
            15 => inner.int_level,
            // UARTMIS
            16 => inner.int_level & inner.int_enabled,
            // UARTDMACR
            18 => inner.dmacr,
            0x3f8..=0x400 => inner.id[((offset - 0xfe0) >> 2) as usize] as u32,
            _ => {
                log_guest_error!("pl011_read: Bad offset 0x{:x}", offset);
                0
            },
        };
        Pl011StateTrace::trace_pl011_read(&offset, &(value as u64));
        value as u64
    }

    fn write(&self, offset: u64, data: u64, _: usize) {
        Pl011StateTrace::trace_pl011_write(&offset, &data);

        let value = data as u32;
        let mut inner = self.inner.lock().unwrap();
        match offset >> 2 {
            // UARTDR
            0 => {
                let ch = [data as u8];
                let _ = self.interface.char_backend_device.chr_write(&ch);
                inner.int_level |= PL011_INT_TX;
                inner.pl011_update(&self.interface);
            },
            // UARTRSR/UARTECR
            1 => inner.rsr = 0,
            // UARTFR
            6 => {},
            // UARTUARTILPR
            8 => inner.ilpr = value,
            // UARTIBRD
            9 => {
                inner.ibrd = value;
                inner.pl011_trace_baudrate_change(&self.interface);
            },
            // UARTFBRD
            10 => {
                inner.fbrd = value;
                inner.pl011_trace_baudrate_change(&self.interface);
            },
            // UARTLCR_H
            11 => {
                // Reset the FIFO state on FIFO enable or disable
                if (inner.lcr ^ value) & 0x10 != 0 {
                    inner.pl011_reset_fifo();
                }
                if (inner.lcr ^ value) & 0x1 != 0 {
                    let mut break_enable: Box<dyn Any> = Box::new(data & 0x1);
                    let _ = self
                        .interface
                        .char_backend_device
                        .chr_ioctl(chardev::CharDeviceIoctl::SerialSetBreak, &mut break_enable);
                }
                inner.lcr = value;
                inner.pl011_set_read_trigger();
            },
            // UARTCR
            12 => inner.cr = value,
            // UARTIFS
            13 => {
                inner.ifl = value;
                inner.pl011_set_read_trigger();
            },
            // UARTIMSC
            14 => {
                inner.int_enabled = value;
                inner.pl011_update(&self.interface);
            },
            // UARTICR
            17 => {
                inner.int_level &= !value;
                inner.pl011_update(&self.interface);
            },
            // UARTDMACR
            18 => {
                inner.dmacr = value;
                if value & 3 != 0 {
                    log_unimp!("pl011: DMA not implemented");
                }
            },
            _ => {
                log_guest_error!("pl011_write: Bad offset 0x{:x}", offset);
            },
        }
    }

    fn endianness(&self) -> memory::DeviceEndian {
        memory::DeviceEndian::NativeEndian
    }

    fn region_size(&self) -> usize {
        0x1000
    }
}

impl ObjectId for Pl011State {
    fn compatible(&self) -> &'static str {
        if self.inner.lock().unwrap().id == PL011_ID_ARM { "pl011" } else { "pl011_luminary" }
    }

    fn id(&self) -> &str {
        &self.name
    }
}

impl CharBackendImpl for Pl011State {
    fn backend_can_recv(&self) -> usize {
        let inner = self.inner.lock().unwrap();
        let r = inner.read_count < inner.pl011_get_fifo_depth() as i32;
        Pl011StateTrace::trace_pl011_can_receive(&inner.lcr, &inner.read_count, &(r as u64));
        if r { 1 } else { 0 }
    }

    fn backend_event(&self, event: chardev::CharDeviceEvent) {
        if event == chardev::CharDeviceEvent::EventBreak {
            self.pl011_put_fifo(0x400);
        }
    }

    fn backend_receive(&self, buf: &[u8]) {
        self.pl011_put_fifo(buf[0] as u32);
    }
}

impl ClkEventImpl for Pl011State {
    fn notify_clock_upadte(&self, _: clock::ClkEvent) {
        let inner = self.inner.lock().unwrap();
        inner.pl011_trace_baudrate_change(&self.interface);
    }
}

impl DeviceManageImpl for Pl011State {
    fn reset(&self, _reset: DeviceResetType) {
        self.reset();
    }
}

/// pl011 硬件构建器
#[derive(Default)]
pub struct Pl011StateBuilder {
    irq: Option<[Arc<IntBackendDevice>; 6]>,
    char_backend_device: Option<Arc<CharBackendDevice>>,
    clk_input: Option<Arc<HwClockInput>>,
    id: Option<[u8; 8]>,
    name: String,
}

impl Pl011StateBuilder {
    /// 创建一个构建器
    pub fn builder() -> Self {
        Self::default()
    }

    /// 设置 irq 接口
    pub fn set_irq(mut self, irq: &[Arc<IntBackendDevice>; 6]) -> Self {
        self.irq = Some(irq.clone());
        self
    }

    /// 设置字符后端设备
    pub fn set_char_backend_device(mut self, char_backend_device: Arc<CharBackendDevice>) -> Self {
        self.char_backend_device = Some(char_backend_device);
        self
    }

    /// 设置时钟输入
    pub fn clk_input(mut self, clk_input: Arc<HwClockInput>) -> Self {
        self.clk_input = Some(clk_input);
        self
    }

    /// 设置为 pl011
    pub fn type_pl011(mut self) -> Self {
        self.id = Some(PL011_ID_ARM);
        self
    }

    /// 设置为 `pl011_luminary`
    pub fn type_pl011_luminary(mut self) -> Self {
        self.id = Some(PL011_ID_LUMINARY);
        self
    }

    /// 为 pl011 设置一个全局名字
    pub fn name(mut self, name: &str) -> Self {
        self.name = String::from(name);
        self
    }

    /// 构建一个 pl011
    ///
    /// # Panics
    /// 如果配置不符合预期将会 panic
    pub fn build(self) -> Pl011State {
        let inner = Pl011StateInner { id: self.id.unwrap(), ..Default::default() };
        Pl011State {
            inner: Arc::new(Mutex::new(inner)),
            interface: Box::new(Pl011StateInterface {
                irq: self.irq.unwrap(),
                char_backend_device: self.char_backend_device.unwrap(),
                clk_input: self.clk_input.unwrap(),
            }),
            name: self.name,
        }
    }
}
